#if !UNITY_WEBGL || UNITY_EDITOR using System; using BestHTTP.Core; namespace BestHTTP.Connections { /// <summary> /// Represents and manages a connection to a server. /// </summary> public sealed class HTTPConnection : ConnectionBase { public TCPConnector connector; public IHTTPRequestHandler requestHandler; public override TimeSpan KeepAliveTime { get { if (this.requestHandler != null && this.requestHandler.KeepAlive != null) return this.requestHandler.KeepAlive.TimeOut; return base.KeepAliveTime; } protected set { base.KeepAliveTime = value; } } public override bool CanProcessMultiple { get { if (this.requestHandler != null) return this.requestHandler.CanProcessMultiple; return base.CanProcessMultiple; } } internal HTTPConnection(string serverAddress) :base(serverAddress) {} internal override void Process(HTTPRequest request) { this.LastProcessedUri = request.CurrentUri; if (this.requestHandler == null || !this.requestHandler.HasCustomRequestProcessor) base.Process(request); else { this.requestHandler.Process(request); LastProcessTime = DateTime.Now; } } protected override void ThreadFunc() { if (this.connector != null && !this.connector.IsConnected) { // this will semd the request back to the queue RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(CurrentRequest, RequestEvents.Resend)); ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed)); return; } if (this.connector == null) { this.connector = new Connections.TCPConnector(); try { this.connector.Connect(this.CurrentRequest); } catch(Exception ex) { HTTPManager.Logger.Exception("HTTPConnection", "Connector.Connect", ex); this.CurrentRequest.State = HTTPRequestStates.ConnectionTimedOut; ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed)); return; } #if !NETFX_CORE // data sending is buffered for all protocols, so when we put data into the socket we want to send them asap this.connector.Client.NoDelay = true; #endif StartTime = DateTime.UtcNow; HTTPManager.Logger.Information("HTTPConnection", "Negotiated protocol through ALPN: '" + this.connector.NegotiatedProtocol + "'"); switch (this.connector.NegotiatedProtocol) { case HTTPProtocolFactory.W3C_HTTP1: this.requestHandler = new Connections.HTTP1Handler(this); ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HostProtocolSupport.HTTP1)); break; #if (!UNITY_WEBGL || UNITY_EDITOR) && !BESTHTTP_DISABLE_ALTERNATE_SSL && !BESTHTTP_DISABLE_HTTP2 case HTTPProtocolFactory.W3C_HTTP2: this.requestHandler = new Connections.HTTP2.HTTP2Handler(this); this.CurrentRequest = null; ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HostProtocolSupport.HTTP2)); break; #endif default: HTTPManager.Logger.Error("HTTPConnection", "Unknown negotiated protocol: " + this.connector.NegotiatedProtocol); RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(CurrentRequest, RequestEvents.Resend)); ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed)); return; } } this.requestHandler.RunHandler(); LastProcessTime = DateTime.Now; } public override void Shutdown(ShutdownTypes type) { base.Shutdown(type); if (this.requestHandler != null) this.requestHandler.Shutdown(type); switch(this.ShutdownType) { case ShutdownTypes.Immediate: this.connector.Dispose(); break; } } protected override void Dispose(bool disposing) { LastProcessedUri = null; if (this.State != HTTPConnectionStates.WaitForProtocolShutdown) { if (this.connector != null) { try { this.connector.Close(); } catch { } this.connector = null; } if (this.requestHandler != null) { try { this.requestHandler.Dispose(); } catch { } this.requestHandler = null; } } else { // We have to connector to do not close its stream at any cost while disposing. // All references to this connection will be removed, so this and the connector may be be finalized after some time. // But, finalizing (and disposing) the connector while the protocol is still active would be fatal, // so we have to make sure that it will not happen. This also means that the protocol has the resposibility (as always had) // to close the stream and TCP connection properly. if (this.connector != null) this.connector.LeaveOpen = true; } base.Dispose(disposing); } } } #endif